package components
{	
	import caurina.transitions.Tweener;
	
	import classes.ReturnedImage;
	
	import flash.events.MouseEvent;
	import flash.geom.Point;
	
	import mx.collections.ArrayCollection;
	import mx.containers.Canvas;
	import mx.controls.Image;
	import mx.core.DragSource;
	import mx.events.DragEvent;
	import mx.managers.DragManager;
	import mx.managers.PopUpManager;

	public class ImagesPlot extends Canvas
	{
		private var _thumbWidth:Number = 200;
		private var _thumbHeight:Number = 200;
		private var _numberOfImages:Number = 0;
		private var previousNumberOfImages:Number = 0;
		private var _magicNumber:Number = 1.5;
		private var _scaleFactor:Number = 0.5;
		private var pp:ImagePopup = new ImagePopup();
		private var _showTiledView:Boolean = false;
		
		[ArrayElementType("ReturnedImage")]
		private var _dataProvider:ArrayCollection;
		private var dataProviderModified:Boolean = false;
		
		private var _filteredTags:ArrayCollection;
		private var _filteredTagsChanged:Boolean = false;
		
		public function get scaleFactor():Number{
			return _scaleFactor;
		}
		
		public function set scaleFactor(value:Number):void{
			_scaleFactor=value;
			invalidateProperties();
			invalidateDisplayList();
		}
		
		public function get showTiledView():Boolean{
			return _showTiledView;
		}
		
		public function set showTiledView(value:Boolean):void{
			_showTiledView = value;
			invalidateProperties();
			invalidateDisplayList();
		}
		
		public function get magicNumber():Number{
			return _magicNumber;
		}
		
		public function set magicNumber(value:Number):void{
			this._magicNumber = value;
			invalidateProperties();
			invalidateDisplayList();
		}
		
		public function get dataProvider():ArrayCollection{
			return _dataProvider;
		}
		
		public function set dataProvider(value:ArrayCollection):void{
			_dataProvider = value;
			this.filteredTags = new ArrayCollection();
			dataProviderModified = true;
			invalidateProperties();
			invalidateSize();
			invalidateDisplayList();
		}
		
		public function get thumbWidth():Number{
			return _thumbWidth;
		}
		
		public function set thumbWidth(value:Number):void{
			_thumbWidth = value;
			invalidateProperties();
			invalidateDisplayList();
		}
		
		public function get thumbHeight():Number{
			return _thumbWidth;
		}
		
		public function set thumbHeight(value:Number):void{
			_thumbHeight = value;
			invalidateProperties();
			invalidateDisplayList();
		}
		
		public function get numberOfImages():Number{
			return _numberOfImages;
		}
		
		public function set numberOfImages(value:Number):void{
			if(value != numberOfImages){
				previousNumberOfImages = _numberOfImages;
				_numberOfImages = value;
				invalidateProperties();
				invalidateSize();
				invalidateDisplayList();
			}
		}
		
		[Bindable ("filteredTagsChanged")]
		public function get filteredTags():ArrayCollection{
			return _filteredTags;
		}
		
		public function set filteredTags(value:ArrayCollection):void{
			if(value != _filteredTags){
				_filteredTags = value;
				_filteredTagsChanged = true;
				invalidateProperties();
				invalidateSize();
				invalidateDisplayList();
			}
		}
		
		private function tagFilter(item:ReturnedImage):Boolean{
			trace(item.title + ": " + item.tags.toString());
			for each(var imgTag:String in item.tags){
				for each(var filterTag:String in filteredTags){
					if(imgTag==filterTag){
						trace(imgTag + "=" + filterTag);
						return true;	
					}
					trace(imgTag + "!=" + filterTag);
				}
			}
			return false;
		}
		
		public function ImagesPlot(){
			super();
			this.verticalScrollPolicy = "off";
			this.horizontalScrollPolicy = "off";
			this.setStyle("backgroundColor", 0xFFFFFF);
			this.setStyle("backgroundAlpha", 0);
			this.clipContent = false;
			this._dataProvider = new ArrayCollection();
			this._filteredTags = new ArrayCollection();
			this.addEventListener(DragEvent.DRAG_ENTER, dragEnterHandler);
			this.addEventListener(DragEvent.DRAG_DROP, dragDropHandler);			
		}
		
		public function addFilteredTag(theTag:String):void{
			if(!this._filteredTags.contains(theTag)){
				activateFiltering();
				this._filteredTags.addItem(theTag);
				_filteredTagsChanged = true;
				invalidateProperties();
				invalidateSize();
				invalidateDisplayList();
			}
		}
		
		public function removeFilteredTag(theTag:String):void{
			if(this._filteredTags.contains(theTag)){
				this._filteredTags.removeItemAt(this._filteredTags.getItemIndex(theTag));
				_filteredTagsChanged = true;
			}
			if(this._filteredTags.length==0){
				deactivateFiltering();
			}
			invalidateProperties();
			invalidateSize();
			invalidateDisplayList();
		}
		
		public function activateFiltering():void{
			_dataProvider.filterFunction = tagFilter;	
		}
		
		public function deactivateFiltering():void{
			_dataProvider.filterFunction = null;	
		}
		
		private function dragDropHandler(event:DragEvent):void{
			var p:Point = this.globalToContent(new Point(event.stageX, event.stageY));
			var dI:MyImage = event.dragInitiator as MyImage;
			event.dragInitiator.x = p.x - dI.draggingPoint.x;
			event.dragInitiator.y = p.y - dI.draggingPoint.y;
		}
		
		private function dragEnterHandler(event:DragEvent):void{
			
		//	trace(event.currentTarget);
			var dropTarget:Canvas=event.currentTarget as Canvas;

        // Accept the drag only if the user is dragging data 
        // identified by the 'value' format value.

        // Accept the drop.
           DragManager.acceptDragDrop(dropTarget);
       	}
		
		private function handleMouseMove(event:MouseEvent):void{
			var dragInitiator:MyImage = event.currentTarget as MyImage;
			dragInitiator.hasBeenDragged = true;
			var dragSource:DragSource = new DragSource();
			var proxyImg:Image = new Image;
			proxyImg.source = dragInitiator.img.thumbURL
			proxyImg.width = dragInitiator.width;
			proxyImg.height = dragInitiator.height;
			proxyImg.scaleContent = true;
		//	trace(index);
		//	trace("filename: "  + dataProvider.getItemAt(index).title, "thumbUrl: " + dataProvider.getItemAt(index).thumbUrl, "link: " + dataProvider.getItemAt(index).url);
			dragSource.addData(dragInitiator.img, "draggedImg");
			DragManager.doDrag(dragInitiator, dragSource, event, proxyImg);
		}
		
		private function handleMouseDown(event:MouseEvent):void{
			var dragInitiator:MyImage = event.currentTarget as MyImage;
			dragInitiator.draggingPoint = this.globalToContent(new Point(event.stageX, event.stageY));
		//	trace("dragPoint: " + this.globalToContent(new Point(event.stageX, event.stageY)));
		//	trace("dragInitX: " + dragInitiator.x, "dragInitY: " + dragInitiator.y);
			dragInitiator.draggingPoint.x -= dragInitiator.x;
			dragInitiator.draggingPoint.y -= dragInitiator.y;
		//	trace("dragInitPosX:" + dragInitiator.draggingPoint.x, "dragInitPosY:" + dragInitiator.draggingPoint.y);
		}
		
		private function showPopUp(event:MouseEvent):void{
			pp.image = event.currentTarget.img;
			PopUpManager.addPopUp(pp, this.parent, false);
		}
		
        protected function scalingFactor(img:ReturnedImage):Number{
        	return ((_scaleFactor)*img.clippedSimilarity + (1 - _scaleFactor));
        }
        
        protected function computeX(boxWidth:Number, image:ReturnedImage):Number{
        //	trace("BoxWidth: " + boxWidth + " imageWidth: " + image.width + " clippedScore: " + image.clippedScore);
        	
        //	Calcolo roba strana
 			var x:Number;

        	if(image.width > image.height){
        		x = (image.clippedScore +1)*(boxWidth - thumbWidth*scalingFactor(image))/2;
        	//	trace("X: " + x);
			}
			else{
			//	trace(scalingFactor(image)/2);				
				var scaledPicWidth:Number = thumbHeight*image.width/image.height;
	        	x = (image.clippedScore +1)*(boxWidth - scaledPicWidth*scalingFactor(image))/2;
        	//	trace("X: " + x);
			}
        	return x;  
        }
        
        protected function computeY(boxHeight:Number, image:ReturnedImage):Number{
        //	trace("BoxHeight: " + boxHeight + " imageHeight: " + image.height + " clippedSimilarity: " + image.clippedSimilarity);
        	var y:Number;
        	
        //	Calcolo roba strana

        	if(image.width < image.height){
				y = image.clippedSimilarity*(boxHeight - thumbHeight*scalingFactor(image));
			}
			else{
				var scaledPicHeight:Number = thumbWidth*image.height/image.width;
	        	y = image.clippedSimilarity*(boxHeight - scaledPicHeight*scalingFactor(image));
			}
        //	trace("Y: " + y);
        	return y;
        }		
		
		override protected function createChildren():void{
			super.createChildren();
		//	trace("createChildren()");
		//	Chiamato solo all'aggiunta del componente
		}
		
		override protected function commitProperties():void{
			super.commitProperties();
		//	trace("commitProperties()");
		//	Chiamato ogni volta che viene modificata una proprietà:

		//	Modificato _numberOfImages:===============================
		//	var sort:Sort = new Sort();
		//	sort.fields = [new SortField("globalRank",false,false)];
		//	dataProvider.sort = sort;
			dataProvider.refresh();
			
		//	trace(dataProvider.toString());
			//	Elimino tutti i vecchi children
			var children:Array = this.getChildren().sort(sorter);
						
			for each(var child:MyImage in children){
				if(!dataProvider.contains(child.img)){
					this.removeChild(child);
				}
			}
			children.sort(sorter);
			
			for(var i:int=numberOfImages; i<children.length; i++){
				this.removeChild(children[i] as MyImage);
			} 

	//		Inserisco al max numberOfImages children
			for(var j:Number=0;j<numberOfImages && j<dataProvider.length; j++){
				if(!alreadyShown(dataProvider.getItemAt(j) as ReturnedImage)){
					var image:MyImage = new MyImage();
					image.x = unscaledWidth/2;
					image.img = dataProvider.getItemAt(j) as ReturnedImage;
					image.addEventListener(MouseEvent.CLICK, showPopUp);
					image.addEventListener(MouseEvent.MOUSE_MOVE, handleMouseMove);
					image.addEventListener(MouseEvent.MOUSE_DOWN, handleMouseDown);
					this.addChildAt(image,0);
				}
			}

			for each(var img:MyImage in this.getChildren()){
				img.hasBeenDragged = false;
			}
			previousNumberOfImages = numberOfImages;
			dataProviderModified=false;
			_filteredTagsChanged = false;
			doClipDistance();
			doClipScore();
		}
		
		private function sorter(a:MyImage, b:MyImage):Number{
			if(a.img.globalRank > b.img.globalRank)
				return +1;
			if(a.img.globalRank == b.img.globalRank)
				return 0;
			return -1;
		}
		
		private function alreadyShown(img:ReturnedImage):Boolean{
			for each(var myImg:MyImage in this.getChildren()){
				if(myImg.img == img)
					return true;
			}
			return false;
		}
		
		private function doClipDistance():void{
			var max:Number = Number.NEGATIVE_INFINITY;
			var min:Number = Number.POSITIVE_INFINITY;
			var currentImg:ReturnedImage;
			
			//Log:
			for(var i:Number=0; i<numberOfImages && i<dataProvider.length; i++){
				currentImg = dataProvider.getItemAt(i) as ReturnedImage;
				currentImg.clippedSimilarity = (- Math.log(currentImg.similarity))*(currentImg.globalRank  + magicNumber);
			}			
			
			//Trovo max e min:
			for(var j:Number=0; j<numberOfImages &&  j<dataProvider.length; j++){
				currentImg = dataProvider.getItemAt(j) as ReturnedImage;
				if(currentImg.clippedSimilarity >= max){
					max = currentImg.clippedSimilarity;
				}
				if(currentImg.clippedSimilarity <= min){
					min = currentImg.clippedSimilarity;
				}
			}
			
			//Clipping:
			for(var k:Number=0; k<numberOfImages && k<dataProvider.length; k++){
				currentImg = dataProvider.getItemAt(k) as ReturnedImage;
				if(max != min){
					currentImg.clippedSimilarity = (1 - (currentImg.clippedSimilarity - min)/(max - min));
				}else{
					currentImg.clippedSimilarity = 0.5;
				}
			}
			
		}
		
		private function doClipScore():void{
			var min:Number = Number.POSITIVE_INFINITY;
			var max:Number = Number.NEGATIVE_INFINITY;
			var isRightSideBigger:Boolean = false;
			var currentImg:ReturnedImage;
			var currentScore:Number;
			
			//trovo max e min:
			for(var i:Number=0; i<numberOfImages && i<dataProvider.length; i++){
				currentImg = dataProvider.getItemAt(i) as ReturnedImage;
				currentScore = currentImg.textureScore - currentImg.colorScore;
				
				if(currentScore >= max){
					max = currentScore;
				}
				if(currentScore <= min){
					min = currentScore;
				}
			}
		//	trace("Max: " + max, "Min: " + min);
			
			//vari casi:
			//max e min sono = in valore assoluto
			if(Math.abs(min) == Math.abs(max)){
				//Clipping tra -1 e +1
				for(var j:Number=0; j<numberOfImages && j<dataProvider.length; j++){
					currentImg = dataProvider.getItemAt(j) as ReturnedImage;
					currentScore = currentImg.textureScore - currentImg.colorScore;
					currentImg.clippedScore = currentScore*(1/Math.abs(max));
					trace("CurrentScore: " + currentScore, "ClippedScore: " + currentImg.clippedScore);
				}
			}
			//max e min hanno lo stesso segno e sono a dx:
			else if(max > 0 && min > 0){
				//Clipping tra 0 e +1
				for(var k:Number=0; k<numberOfImages && k<dataProvider.length; k++){
					currentImg = dataProvider.getItemAt(k) as ReturnedImage;
					currentScore = currentImg.textureScore - currentImg.colorScore;
					currentImg.clippedScore = (currentScore - min)/(max - min);
					trace("CurrentScore: " + currentScore, "ClippedScore: " + currentImg.clippedScore);
				}
			}
			//max e min hanno lo stesso segno e sono a sx:
			else if(max < 0 && min < 0){
				//Clipping tra -1 e 0
				for(var l:Number=0; l<numberOfImages && l<dataProvider.length; l++){
					currentImg = dataProvider.getItemAt(l) as ReturnedImage;
					currentScore = currentImg.textureScore - currentImg.colorScore;
					currentImg.clippedScore = (currentScore - min)/(max - min) - 1;
					trace("CurrentScore: " + currentScore, "ClippedScore: " + currentImg.clippedScore);
				}
			}
			//max e min hanno segno discorde e abs(max)>abs(min)
			else if(Math.abs(max) > Math.abs(min)){
				//Clipping nel semipiano dx tra 0 e 1, nel sx uso lo stesso coeff ang
				for(var m:Number=0; m<numberOfImages && m<dataProvider.length; m++){
					currentImg = dataProvider.getItemAt(m) as ReturnedImage;
					currentScore = currentImg.textureScore - currentImg.colorScore;
					currentImg.clippedScore = currentScore / max;
					trace("CurrentScore: " + currentScore, "ClippedScore: " + currentImg.clippedScore);
				}
			}
			//max e min hanno segno discorde e abs(max)<abs(min)
			else if(Math.abs(max) < Math.abs(min)){
				//Clipping nel semipiano sx tra -1 e 0, nel dx uso lo stess coeff ang
				for(var n:Number=0; n<numberOfImages && n<dataProvider.length; n++){
					currentImg = dataProvider.getItemAt(n) as ReturnedImage;
					currentScore = currentImg.textureScore - currentImg.colorScore;
					currentImg.clippedScore = (min - currentScore)/(min) - 1;
					trace("CurrentScore: " + currentScore, "ClippedScore: " + currentImg.clippedScore);
				}
			}
		}
		
		override protected function measure():void{
			super.measure();
		//	trace("Measure");
			
		//	measuredMinWidth = 0;
		//	measuredMinHeight = 0;
		}
		
		override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void{
			super.updateDisplayList(unscaledWidth, unscaledHeight);
		//	trace("updatedisplay");
			
			//IMPOSTAZIONE E POSIZIONAMENTO IMMAGINI:
			if(dataProvider.length!=0){
				//DISPOSIZIONE NON TILED
				if(!_showTiledView){
					for each (var img:MyImage in this.getChildren()){
						var correspondingReturnedImage:ReturnedImage = img.img as ReturnedImage;
						var scalingFactor:Number = scalingFactor(correspondingReturnedImage);
						Tweener.addTween(img,{width:thumbWidth*scalingFactor, height:thumbHeight*scalingFactor, time:1});
				//		img.setActualSize(thumbWidth*scalingFactor,thumbHeight*scalingFactor);
						if(img.hasBeenDragged == false){
							Tweener.addTween(img, {x: computeX(unscaledWidth, correspondingReturnedImage), y: computeY(unscaledHeight, correspondingReturnedImage), time:1});
						}
					}	
				}else{
					var picDimension:Number=thumbWidth; //SI ASSUME CHE WIDTH E HEIGHT SIANO UGUALI
					var columnNumber:Number = Math.floor(unscaledWidth/picDimension);
					var rowNumber:Number = Math.ceil(numberOfImages/columnNumber);
					while(rowNumber*picDimension > unscaledHeight){
						columnNumber++;
						picDimension = unscaledWidth/columnNumber;
						rowNumber = Math.ceil(numberOfImages/columnNumber);
					}
					var children:Array = this.getChildren();
					children.sort(sorter);
					var i:int=0
					for each(var img:MyImage in children){
						Tweener.addTween(img,{width:picDimension, height:picDimension, time:1});
						Tweener.addTween(img, {x: (i%columnNumber)*picDimension , y: (Math.floor(i/columnNumber))*picDimension, time:1});
						i++;
					}
				}
				
			}
		}
	}
}